﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Server;
using RiaToDalLinq.ProviderBase;
using RiaToDalLinq.Attributes;
using RiaToDalLinq.DomainServices;

namespace RiaToDalLinq
{
    internal sealed class Parameter
    {
        public string Name
        {
            get;
            set;
        }

        public Type Type
        {
            get;
            set;
        }
    }

    /// <summary>
    /// Context for querying a legacy data store
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class RiaToDalQueryContext<T> : RiaToDalQueryContextBase
    {
        private Query<T> _entities;
        private BaseQueryProvider _provider;

        private Dictionary<string, string> _fieldMappings = new Dictionary<string, string>();
        private KeyValuePair<Type, Type> _entityMap;

        private LegacyDalDomainServiceBase _callingDomainService;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="domainService"></param>
        public RiaToDalQueryContext(LegacyDalDomainServiceBase domainService)
        {
            _callingDomainService = domainService;

            Type t = typeof(T);

            //determine entity attribute mappings
            Attribute entityAtribute = Attribute.GetCustomAttribute(t, typeof(EntityMap));
            if (entityAtribute == null)
            {
                throw new Exception(string.Format("EntityMap must be defined for RIA entity type {0}", typeof(T).ToString()));
            }

            //map attributes
            EntityMap entityMap = entityAtribute as EntityMap;
            _entityMap = new KeyValuePair<Type, Type>(t, entityMap.EntityType);

            //create an instance of the DAL entity
            object instance = Activator.CreateInstance(_entityMap.Value);

            //determine field mappings
            PropertyInfo[] properties = typeof(T).GetProperties();

            foreach (PropertyInfo pi in properties)
            {
                FieldMap fieldMap = Attribute.GetCustomAttribute(pi, typeof(FieldMap), false) as FieldMap;

                if (fieldMap == null)
                {
                    //no mapping defined, default to the property name and type
                    if (instance.GetType().GetProperty(pi.Name) == null)
                    {
                        throw new Exception(string.Format("Property {0} does not exist on DAL entity {1}", pi.Name, _entityMap.Value.Name));
                    }
                    else if (instance.GetType().GetProperty(pi.Name).PropertyType != pi.PropertyType)
                    {
                        throw new Exception(string.Format("Types on properties {0}.{1} (RIA Entity)  and {2}.{3} (DAL Entity) do not match ({4} != {5})",
                                _entityMap.Key.Name, pi.Name, _entityMap.Value.Name, pi.Name,
                                pi.PropertyType, instance.GetType().GetProperty(pi.Name).PropertyType
                            ));
                    }

                    continue;
                }

                if (instance.GetType().GetProperty(fieldMap.FieldName) == null)
                {
                    throw new Exception(string.Format("Property {0} does not exist on DAL entity {1}", fieldMap.FieldName, _entityMap.Value.Name));
                }
                else if (instance.GetType().GetProperty(fieldMap.FieldName).PropertyType != pi.PropertyType)
                {
                    throw new Exception(string.Format("Types on properties {0}.{1} (RIA Entity)  and {2}.{3} (DAL Entity) do not match ({4} != {5})",
                            _entityMap.Key.Name, pi.Name, _entityMap.Value.Name, instance.GetType().GetProperty(fieldMap.FieldName).Name,
                            pi.PropertyType, instance.GetType().GetProperty(fieldMap.FieldName).PropertyType
                        ));
                }

                _fieldMappings.Add(pi.Name, fieldMap.FieldName);
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryDescription"></param>
        /// <returns></returns>
        public IQueryable<T> ExecuteQueryFromQueryDescription(QueryDescription queryDescription)
        {
            _provider = new RiaToDalQueryProvider(_entityMap, _fieldMappings, queryDescription, _callingDomainService);
            _entities = new Query<T>(_provider);
            return _entities;
        }


        /// <summary>
        /// The entities that will be returned from the query
        /// </summary>
        public IQueryable<T> Entities
        {
            get
            {
                _provider = new RiaToDalQueryProvider(_entityMap, _fieldMappings, _callingDomainService.ActiveQueryDescription, _callingDomainService);
                _entities = new Query<T>(_provider);
                return _entities;
            }
        }

    }
}
